//gcc main.c tftp.c -o tftp  -lpthread && ls

#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>


#include "tftp.h"


FILE *fp_log;
static volatile int count=0;

unsigned short GetShortAt(char *addr)
{
    return ntohs(*((unsigned short*) addr));
}

void PutShortAt(char *addr, unsigned short sval)
{
    char *ptr;
    short n;
    n=htons(sval);
    ptr=&n;
    addr[0]=*ptr;
    ptr++;
    addr[1]=*ptr;
}

char* errmsg(int errcode)
{
    switch(errcode)
    {
        case File_not_found:
            return "File not found";
        case Access_violation:
            return "Access violation";
        case Disk_full_or_allocation_exceeded:
            return "Disk full or allocation exceeded";
        case Illegal_TFTP_operation:
            return "Illegal TFTP operation";
        case Unknown_transfer_ID:
            return "Unknown transfer ID";
        case File_already_exists:
            return "File already exists";
        case No_such_user:
            return "No such user";
        default :
            return NULL;
    }
}


int do_write(struct sockaddr_in cl_addr,char *filename,int s_l)
{
    int ret_val;
    printf("do_write\n");
    if(fork()==0)
    {
        //close(1);
        //dup2(fileno(fp_log),1);
        ret_val=write_to_remote(cl_addr,filename,s_l);
        exit(ret_val);
    }

    count++;
    return 0;
}


int do_read(struct sockaddr_in cl_addr,char *filename,int s_l)
{
    char str[80];
    int ret_val;
    sprintf(str,"log/d_rlog:%d",count);
    fp_log=fopen(str,"w");
    fflush(stdout);
    if(fork()==0)
    {
        close(1);
        dup2(fileno(fp_log),1);
        ret_val=read_from_remote(cl_addr,filename,s_l);
        exit(ret_val);
    }

    count++;
    return 0;
}


void *thd_caminfo( void *ptr_param )
{
    printf("thd_caminfo In \n");

    int cl_sk;
    char *file_str,buf[512],*ptr;
    unsigned short pkt_no;
    ACK_PKT ap;
    ERR_PKT ep;
    DATA_PKT dp;
    fd_set fds;
    _Bool cnctd,ack_pending;
    struct timeval t;

    
    struct _DATA_CAMINFO *data_caminf;
    struct sockaddr_in cl_addr;
    char *file;
    int s_l;

    
    //copy data
    data_caminf = (struct _DATA_CAMINFO *)ptr_param;
    memcpy(&cl_addr,&(data_caminf->cl_addr),sizeof(struct sockaddr_in));
    s_l = data_caminf->s_l;
    file =data_caminf->file; 
    free(ptr_param);

    print_socket(cl_addr);

    t.tv_sec=TIMEOUT;
    t.tv_usec=0;

    cl_sk=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
    if(cl_sk==-1)
    {
        perror("cl_sk");
        return -1;
    }


    // set timeout for recv() 
    struct timeval tv;
    tv.tv_sec = 2;
    if (setsockopt(cl_sk, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,  sizeof tv))
    {
      perror("setsockopt");
      return -1;
    }





#if 0 //filter it should disable.
    char tmp[512];
    sprintf(tmp,"%s",inet_ntoa(cl_addr.sin_addr));
   if(strcmp(tmp,"192.168.127.58")!=0)
    {
        printf("ignor data %s \r\n",tmp);
        return 0;
    }
   else
            printf("==================ok ok %s \r\n",tmp);
#endif         

    if (strcmp(file, "caminfo") == 0) {
        char *m_ptr = dp.data;
        m_ptr += sprintf(m_ptr, "+VENDOR : 10010101\r\n");
        m_ptr += sprintf(m_ptr, "+MODEL : B2210E\r\n");
        m_ptr += sprintf(m_ptr, "+SERIAL : \r\n");
        m_ptr += sprintf(m_ptr, "+IP : 192.168.205.95\r\n");
        m_ptr += sprintf(m_ptr, "+MAC : 11-00-00-00-00-00\r\n");
        m_ptr += sprintf(m_ptr, "+NAME : LEMUR\r\n");
        m_ptr += sprintf(m_ptr, "+LOCATION : \r\n");
        m_ptr += sprintf(m_ptr, "+FW VER : V1.10.00.12\r\n");
        m_ptr += sprintf(m_ptr, "+PORT : 80\r\n");
        m_ptr += sprintf(m_ptr, "+GATEWAY : 192.168.1.254\r\n");
        m_ptr += sprintf(m_ptr, "+MASK : 255.255.0.0\r\n");
        m_ptr += sprintf(m_ptr, "+SETIP : 1\r\n");
        m_ptr += sprintf(m_ptr, "+UPGRADE : 0\r\n");
        m_ptr += sprintf(m_ptr, "+DHCP : 0\r\n");
        m_ptr += sprintf(m_ptr, "+BRAND : iMege\r\n");
        //printf("return data %s \n", dp.data);
        int sz = strlen(dp.data);
        PutShortAt(&dp.block_no,1);
        PutShortAt(&dp.opcode,DATA);

senddata:
        sendto(cl_sk, &dp, sz+4, MSG_DONTROUTE, &cl_addr, s_l);
        int rb = 0;
        for (;;) {
            sleep(1);
            rb = recv(cl_sk, &ap, sizeof(ACK_PKT), 0);
            if (rb < 0) goto exit; //should resend
            ap.opcode = GetShortAt(&ap.opcode);
            ap.block_no = GetShortAt(&ap.block_no);
            if (ap.opcode == ACK) {

                break;
                //return 0;
            }
        }

    }
exit:    
    printf("thd_caminfo exit \n");
}


int read_from_remote(struct sockaddr_in cl_addr,char *file,int s_l)
{
    int file_r,res,i,cl_sk,len,blk_no,retry_count,time_retry_count;
    char *file_str,buf[512],*ptr;
    unsigned short pkt_no;
    ACK_PKT ap;
    ERR_PKT ep;
    DATA_PKT dp;
    fd_set fds;
    _Bool cnctd;
    struct timeval t;

    t.tv_sec=TIMEOUT;
    t.tv_usec=0;


    cl_sk=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
    if(cl_sk==-1)
    {
        perror("cl_sk");
        return -1;
    }

    printf("Received packet from %s:%d\nData\n\n",
                     inet_ntoa(cl_addr.sin_addr),
                     ntohs(cl_addr.sin_port));
    
    file_str=(char*)malloc(strlen(DIR)+strlen(file)+5);
    strcpy(file_str,DIR);
    strcat(file_str,"/");
    strcat(file_str,file);

    printf("\nread frm remote....file: %s",file_str);
    if(open(file_str,O_RDONLY)!=-1)
    {
        perror("\n file already exist ... ");
        PutShortAt(&ep.opcode,ERROR);
        PutShortAt(&ep.ErrorCode,File_already_exists);
        strcpy(ep.errmsg,errmsg(File_already_exists));
        ep.errmsg[strlen(ep.errmsg)]=0;
        len=4+strlen(ep.errmsg)+1;
        if((res=sendto(cl_sk,&ep,len,0,&cl_addr,s_l))==-1)
        {
            perror ("send ");
        }
        return -1;
    }
    file_r=open(file_str,O_WRONLY|O_CREAT,S_IRWXU);
    if(file_r==-1)
    {
        perror("\n faflisaf ... ");
        PutShortAt(&ep.opcode,ERROR);
        PutShortAt(&ep.ErrorCode,Disk_full_or_allocation_exceeded);
        strcpy(ep.errmsg,errmsg(Disk_full_or_allocation_exceeded));
        ep.errmsg[strlen(ep.errmsg)]=0;
        len=4+strlen(ep.errmsg)+1;
        if((res=sendto(cl_sk,&ep,len,0,&cl_addr,s_l))==-1)
        {
            perror ("send ");
        }
        return -1;
    }

    blk_no=0;
    PutShortAt(&ap.block_no,blk_no);
    PutShortAt(&ap.opcode,ACK);
    if((res=sendto(cl_sk,&ap,sizeof(ap),0,&cl_addr,s_l))==-1)
    {
        perror ("send ");
    }


    cnctd=1;
    retry_count=0;
    time_retry_count=0;

    while(cnctd)
    {
       
        
    /*
        ptr=&cl_addr;

        for(i=0;i<s_l;i++,ptr++)
                printf("\n%x ",*ptr);
    */

        //printf("\n res_snto = %d ",res);
        //fflush(stdout);
        //memset(&cl_addr,0,s_l);
        FD_ZERO (&fds);
        FD_SET(cl_sk,&fds);

        
        time_retry_count=0;
        
        while(1)
        {
            errno=0;
            t.tv_sec=TIMEOUT;
            t.tv_usec=0;
            if((res=(select(FD_SETSIZE,&fds,NULL,NULL,&t)))==-1)
            {
                perror("select caused err..... ");
                if(errno==EINTR)
                    continue;
                close(cl_sk);
                return -1;
            }
            if((res!=0)&&(FD_ISSET(cl_sk,&fds)==1))
            {
                printf("data has come ");
                break;
            }
            else
            {
                printf("\n timeout %d ",time_retry_count);
                if(time_retry_count<MAX_TIMEOUT_RETRIES)
                {
                    time_retry_count++;
                    continue;
                }
                close(cl_sk);
                return -2;
            }
        }
        if((res=recvfrom(cl_sk,&dp,sizeof(dp),0,&cl_addr,&s_l))==-1)
        {
                perror("\n recv");
                return -1;
        }
        printf("\n res = %d ",res);
        pkt_no=GetShortAt(&dp.block_no);
        if(pkt_no!=(blk_no+1)%(65536))
        {
            if(retry_count>=MAX_ACK_RETRIES)
            {
                PutShortAt(&ep.opcode,ERROR);
                PutShortAt(&ep.ErrorCode,Illegal_TFTP_operation);
                strcpy(ep.errmsg,errmsg(Illegal_TFTP_operation));
                ep.errmsg[strlen(ep.errmsg)]=0;
                len=4+strlen(ep.errmsg)+1;
                if((res=sendto(cl_sk,&ep,len,0,&cl_addr,s_l))==-1)
                {
                    perror ("send ");
                }
                return -1;
            }
            PutShortAt(&ap.block_no,blk_no);
            PutShortAt(&ap.opcode,ACK);
            if((res=sendto(cl_sk,&ap,sizeof(ap),0,&cl_addr,s_l))==-1)
            {
                perror ("send ");
            }
            retry_count++;
            continue;
        }

        blk_no=pkt_no;
        if(write(file_r,dp.data,res-4)==-1)
        {
            perror("err....wr ");
            return -1;
        }
        if(res<sizeof(dp))
        {
            cnctd=0;
        }
        PutShortAt(&ap.block_no,blk_no);
        PutShortAt(&ap.opcode,ACK);
        if((res=sendto(cl_sk,&ap,sizeof(ap),0,&cl_addr,s_l))==-1)
        {
            perror ("send ");
        }
        printf("\n read n written %d and file size = %d ",blk_no,lseek(file_r,0,SEEK_CUR));
        fflush(stdout);
    }
    //res=recvfrom(t_s,buf,512,0)
    return 0;
}





int write_to_remote(struct sockaddr_in cl_addr,char *file,int s_l)
{
    int file_r,res,i,cl_sk,len,blk_no,retry_count,time_retry_count,pck_len;
    char *file_str,buf[512],*ptr,tmp[512];
    unsigned short pkt_no;
    ACK_PKT ap;
    ERR_PKT ep;
    DATA_PKT dp;
    fd_set fds;
    _Bool cnctd,ack_pending;
    struct timeval t;

    t.tv_sec=TIMEOUT;
    t.tv_usec=0;
    printf("write_to_remote\n");

    cl_sk=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
    if(cl_sk==-1)
    {
        perror("cl_sk");
        return -1;
    }

    printf("Received packet from %s:%d\nData\n\n",
                     inet_ntoa(cl_addr.sin_addr),
                     ntohs(cl_addr.sin_port));


/*
    sprintf(tmp,"%s",inet_ntoa(cl_addr.sin_addr));
   if(strcmp(tmp,"192.168.127.59")!=0)
    {
        printf("ignor data %s \r\n",tmp);
        return 0;
    }
   else
            printf("==================ok ok %s \r\n",tmp);
*/            

    if (strcmp(file, "caminfo") == 0) {
        char *m_ptr = dp.data;
        m_ptr += sprintf(m_ptr, "+VENDOR : iMege\r\n");
        m_ptr += sprintf(m_ptr, "+MODEL : B5000\r\n");
        m_ptr += sprintf(m_ptr, "+SERIAL : 12345678\r\n");
        m_ptr += sprintf(m_ptr, "+IP : 192.168.205.95\r\n");
        m_ptr += sprintf(m_ptr, "+MAC : 11-00-00-00-00-00\r\n");
        m_ptr += sprintf(m_ptr, "+NAME : LEMUR\r\n");
        m_ptr += sprintf(m_ptr, "+LOCATION : \r\n");
        m_ptr += sprintf(m_ptr, "+FW VER : V0.01\r\n");
        m_ptr += sprintf(m_ptr, "+PORT : 80\r\n");
        m_ptr += sprintf(m_ptr, "+GATEWAY : 192.168.1.254\r\n");
        m_ptr += sprintf(m_ptr, "+MASK : 255.255.0.0\r\n");
        m_ptr += sprintf(m_ptr, "+SETIP : 1\r\n");
        m_ptr += sprintf(m_ptr, "+UPGRADE : 0\r\n");
        m_ptr += sprintf(m_ptr, "+DHCP : 0\r\n");
        m_ptr += sprintf(m_ptr, "+BRAND : iMege\r\n");
        printf("return data %s\n", dp.data);
        int sz = strlen(dp.data);
        PutShortAt(&dp.block_no,1);
        PutShortAt(&dp.opcode,DATA);

senddata:
        sendto(cl_sk, &dp, sz+4, MSG_DONTROUTE, &cl_addr, s_l);
        int rb = 0;
        for (;;) {
            sleep(1);
            printf("==================%d: wait ack\n",getpid());
            rb = recv(cl_sk, &ap, sizeof(ACK_PKT), 0);

            if (rb < 0) return -1;
            ap.opcode = GetShortAt(&ap.opcode);
            ap.block_no = GetShortAt(&ap.block_no);
            if (ap.opcode == ACK) {
                 printf("==================Get ACK ap.block_no=%d \n", ap.block_no);
                if(ap.block_no == 1) return 0;
                if(ap.block_no == 0) goto senddata;
            }
            printf("==================\n");
        }
        return -1;
    }
    file_str=(char*)malloc(strlen(DIR)+strlen(file)+5);
    strcpy(file_str,DIR);
    strcat(file_str,"/");
    strcat(file_str,file);

    printf("\nwrtoremote..... file: %s",file_str);
    if((file_r=open(file_str,O_RDONLY))==-1)
    {
        perror("\n file already exist ... ");
        PutShortAt(&ep.opcode,ERROR);
        PutShortAt(&ep.ErrorCode,File_not_found);
        strcpy(ep.errmsg,errmsg(File_not_found));
        ep.errmsg[strlen(ep.errmsg)]=0;
        len=4+strlen(ep.errmsg)+1;
        if((res=sendto(cl_sk,&ep,len,0,&cl_addr,s_l))==-1)
        {
            perror ("send ");
        }
        return -1;
    }
    



    blk_no=1;
    cnctd=1;
    retry_count=0;
    time_retry_count=0;
    ack_pending=0;

    while(cnctd)
    {
        

        if((res=read(file_r,buf,512))==-1)
        {
            perror("read.. ");
            PutShortAt(&ep.opcode,ERROR);
            PutShortAt(&ep.ErrorCode,Disk_full_or_allocation_exceeded);
            strcpy(ep.errmsg,errmsg(Disk_full_or_allocation_exceeded));
            ep.errmsg[strlen(ep.errmsg)]=0;
            len=4+strlen(ep.errmsg)+1;
            if((res=sendto(cl_sk,&ep,len,0,&cl_addr,s_l))==-1)
            {
                perror ("send ");
            }
            return -1;
        }

        printf("\n read bytes frm file= %d ",res);

        PutShortAt(&dp.block_no,blk_no);
        PutShortAt(&dp.opcode,DATA);
        memcpy(&dp.data,buf,res);
        pck_len=res+4;
        if(pck_len<sizeof(dp))
        {
            cnctd=0;
        }
/*
        PutShortAt(&dp.block_no,blk_no);
        PutShortAt(&dp.opcode,DATA);
        memcpy(&dp.data,buf,res);
        if((res=sendto(cl_sk,&dp,res+4,0,&cl_addr,s_l))==-1)
        {
            perror ("send ");
        }
        printf("\n read n written %d and file size = %d ",blk_no,lseek(file_r,0,SEEK_CUR));
*/

        
        //memset(&cl_addr,0,s_l);
        FD_ZERO (&fds);
        FD_SET(cl_sk,&fds);

        
        time_retry_count=0;

        while(1)
        {
            if((res=sendto(cl_sk,&dp,pck_len,0,&cl_addr,s_l))==-1)
            {
                perror ("send ");
            }
            printf("\n read n written %d and file size = %d ",blk_no,lseek(file_r,0,SEEK_CUR));
            t.tv_sec=TIMEOUT;
            t.tv_usec=0;
            errno=0;
            if((res=(select(FD_SETSIZE,&fds,NULL,NULL,&t)))==-1)
            {
                if(errno==EINTR)
                    continue;
                perror("select caused err..... ");
                close(cl_sk);
                return -1;
            }
            if((res!=0)&&(FD_ISSET(cl_sk,&fds)==1))
            {
                printf("data has come ");
                if((res=recvfrom(cl_sk,&ap,sizeof(ap),0,&cl_addr,&s_l))==-1)
                {
                        perror("\n recv");
                        return -1;
                }
                printf("\n size of recv pkt = %d ",res);

                if(GetShortAt(&ap.opcode)!=ACK)
                {
                    perror("invalid packet");
                    PutShortAt(&ep.opcode,ERROR);
                    PutShortAt(&ep.ErrorCode,Illegal_TFTP_operation);
                    strcpy(ep.errmsg,errmsg(Illegal_TFTP_operation));
                    ep.errmsg[strlen(ep.errmsg)]=0;
                    len=4+strlen(ep.errmsg)+1;
                    if((res=sendto(cl_sk,&ep,len,0,&cl_addr,s_l))==-1)
                    {
                        perror ("send ");
                    }
                    return -1;
                }


                pkt_no=GetShortAt(&ap.block_no);

                if(pkt_no!=(blk_no))
                {                    
                    if(retry_count>=MAX_ACK_RETRIES)
                    {
                        PutShortAt(&ep.opcode,ERROR);
                        PutShortAt(&ep.ErrorCode,Illegal_TFTP_operation);
                        strcpy(ep.errmsg,errmsg(Illegal_TFTP_operation));
                        ep.errmsg[strlen(ep.errmsg)]=0;
                        len=4+strlen(ep.errmsg)+1;
                        if((res=sendto(cl_sk,&ep,len,0,&cl_addr,s_l))==-1)
                        {
                            perror ("send ");
                        }
                        return -1;
                    }
        /*
                    PutShortAt(&dp.block_no,blk_no);
                    PutShortAt(&dp.opcode,DATA);
                    memcpy(&dp.data,buf,res);
                    pck_len=res+4;
        */
                    retry_count++;
                    ack_pending=1;
                    continue;
                }

                break;
            }
            else
            {
                printf("\n timeout %d",time_retry_count);
                if(time_retry_count<MAX_TIMEOUT_RETRIES)
                {
                    time_retry_count++;
                    continue;
                }
                close(cl_sk);
                return -2;
            }
        }
                               
        ack_pending=0;
        retry_count=0;
        blk_no=(blk_no+1)%65536;        
        
        fflush(stdout);
    }
    //res=recvfrom(t_s,buf,512,0)
    return 0;
}
